#ifndef GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66

#if defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
#pragma once
#endif

#include "yaml-cpp/mark.h"
#include <string>

namespace YAML {
class Parser;

// GraphBuilderInterface
// . Abstraction of node creation
// . pParentNode is always NULL or the return value of one of the NewXXX()
//   functions.
class GraphBuilderInterface {
public:
	// Create and return a new node with a null value.
	virtual void *NewNull(const Mark& mark, void *pParentNode) = 0;

	// Create and return a new node with the given tag and value.
	virtual void *NewScalar(const Mark& mark, const std::string& tag,
			void *pParentNode, const std::string& value) = 0;

	// Create and return a new sequence node
	virtual void *NewSequence(const Mark& mark, const std::string& tag,
			void *pParentNode) = 0;
	// Add pNode to pSequence.  pNode was created with one of the NewXxx()
	// functions and pSequence with NewSequence().
	virtual void AppendToSequence(void *pSequence, void *pNode) = 0;
	// Note that no moew entries will be added to pSequence
	virtual void SequenceComplete(void *pSequence) {
		(void) pSequence;
	}

	// Create and return a new map node
	virtual void *NewMap(const Mark& mark, const std::string& tag,
			void *pParentNode) = 0;
	// Add the pKeyNode => pValueNode mapping to pMap.  pKeyNode and pValueNode
	// were created with one of the NewXxx() methods and pMap with NewMap().
	virtual void AssignInMap(void *pMap, void *pKeyNode, void *pValueNode) = 0;
	// Note that no more assignments will be made in pMap
	virtual void MapComplete(void *pMap) {
		(void) pMap;
	}

	// Return the node that should be used in place of an alias referencing
	// pNode (pNode by default)
	virtual void *AnchorReference(const Mark& mark, void *pNode) {
		(void) mark;
		return pNode;
	}
};

// Typesafe wrapper for GraphBuilderInterface.  Assumes that Impl defines
// Node, Sequence, and Map types.  Sequence and Map must derive from Node
// (unless Node is defined as void).  Impl must also implement function with
// all of the same names as the virtual functions in GraphBuilderInterface
// -- including the ones with default implementations -- but with the
// prototypes changed to accept an explicit Node*, Sequence*, or Map* where
// appropriate.
template<class Impl>
class GraphBuilder: public GraphBuilderInterface {
public:
	typedef typename Impl::Node Node;
	typedef typename Impl::Sequence Sequence;
	typedef typename Impl::Map Map;

	GraphBuilder(Impl& impl) :
			m_impl(impl) {
		Map* pMap = NULL;
		Sequence* pSeq = NULL;
		Node* pNode = NULL;

		// Type consistency checks
		pNode = pMap;
		pNode = pSeq;
	}

	GraphBuilderInterface& AsBuilderInterface() {
		return *this;
	}

	virtual void *NewNull(const Mark& mark, void* pParentNode) {
		return CheckType<Node>(m_impl.NewNull(mark, AsNode(pParentNode)));
	}

	virtual void *NewScalar(const Mark& mark, const std::string& tag,
			void *pParentNode, const std::string& value) {
		return CheckType<Node>(
				m_impl.NewScalar(mark, tag, AsNode(pParentNode), value));
	}

	virtual void *NewSequence(const Mark& mark, const std::string& tag,
			void *pParentNode) {
		return CheckType<Sequence>(
				m_impl.NewSequence(mark, tag, AsNode(pParentNode)));
	}
	virtual void AppendToSequence(void *pSequence, void *pNode) {
		m_impl.AppendToSequence(AsSequence(pSequence), AsNode(pNode));
	}
	virtual void SequenceComplete(void *pSequence) {
		m_impl.SequenceComplete(AsSequence(pSequence));
	}

	virtual void *NewMap(const Mark& mark, const std::string& tag,
			void *pParentNode) {
		return CheckType<Map>(m_impl.NewMap(mark, tag, AsNode(pParentNode)));
	}
	virtual void AssignInMap(void *pMap, void *pKeyNode, void *pValueNode) {
		m_impl.AssignInMap(AsMap(pMap), AsNode(pKeyNode), AsNode(pValueNode));
	}
	virtual void MapComplete(void *pMap) {
		m_impl.MapComplete(AsMap(pMap));
	}

	virtual void *AnchorReference(const Mark& mark, void *pNode) {
		return CheckType<Node>(m_impl.AnchorReference(mark, AsNode(pNode)));
	}

private:
	Impl& m_impl;

	// Static check for pointer to T
	template<class T, class U>
	static T* CheckType(U* p) {
		return p;
	}

	static Node *AsNode(void *pNode) {
		return static_cast<Node*>(pNode);
	}
	static Sequence *AsSequence(void *pSeq) {
		return static_cast<Sequence*>(pSeq);
	}
	static Map *AsMap(void *pMap) {
		return static_cast<Map*>(pMap);
	}
};

void *BuildGraphOfNextDocument(Parser& parser,
		GraphBuilderInterface& graphBuilder);

template<class Impl>
typename Impl::Node *BuildGraphOfNextDocument(Parser& parser, Impl& impl) {
	GraphBuilder<Impl> graphBuilder(impl);
	return static_cast<typename Impl::Node *>(BuildGraphOfNextDocument(parser,
			graphBuilder));
}
}

#endif // GRAPHBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
